package com.hk.commons.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * Spring Bean 工具类
 *
 * @author kevin
 * @date 2018-04-16 09:41
 */
@Slf4j
@Order(0)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextHolder.applicationContext = applicationContext;
    }

    /**
     * 根据名称获取Bean
     *
     * @param name beanName
     * @return T
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
        return (T) applicationContext.getBean(name);
    }

    public static boolean containsBean(String name) {
        return Objects.nonNull(applicationContext) && applicationContext.containsBean(name);
    }

    public static String getApplicationId() {
        return Objects.nonNull(applicationContext) ? applicationContext.getId() : StringUtils.EMPTY;
    }

    /**
     * 根据Bean类型获取bean
     *
     * @param clazz beanClass
     * @return T
     */
    public static <T> T getBean(Class<T> clazz) throws BeansException {
        return Objects.isNull(applicationContext) ? null : applicationContext.getBean(clazz);
    }

    public static <T> Optional<T> getBeanIfExist(Class<T> clazz) {
        return Optional.ofNullable(Objects.isNull(applicationContext) ?
                null : applicationContext.getBeanProvider(clazz).getIfAvailable());
    }

    public static <T> List<T> getBeans(Class<T> clazz) {
        return applicationContext.getBeanProvider(clazz).orderedStream().toList();
    }

    public static Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) {
        return applicationContext.getBeansWithAnnotation(annotationType);
    }

    public static Environment getEnvironment() {
        return applicationContext.getEnvironment();
    }

    /**
     * 根据类型获取 Bean
     *
     * @param clazz bean 类型
     * @param <T>   T
     * @return Map
     */
    public static <T> Map<String, T> getBeanOfType(Class<T> clazz) {
        return applicationContext.getBeansOfType(clazz);
    }

    /**
     * 根据名称获取 Bean
     *
     * @param name  name
     * @param clazz bean 类型
     * @param <T>   T
     * @return Bean
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return applicationContext.getBean(name, clazz);
    }

    /**
     * 获取环境变量值
     *
     * @param key key
     * @return value
     */
    public static String getEnvironmentValue(String key) {
        return applicationContext.getEnvironment().getProperty(key);
    }

    /**
     * 获取指定类型环境变量值
     *
     * @param key   key
     * @param clazz 值的类型
     * @return value
     */
    public static <T> T getEnvironmentValue(String key, Class<T> clazz) {
        return ConverterUtils.defaultConvert(applicationContext.getEnvironment().getProperty(key), clazz);
    }

    /**
     * 获取激活的配置
     */
    public static String[] getActiveProfiles() {
        return applicationContext.getEnvironment().getActiveProfiles();
    }

    /**
     * 获取国际化消息
     *
     * @param code code
     * @param args args
     * @return i18n Message
     */
    public static String getMessage(String code, Object... args) {
        return getMessageWithDefault(code, code, args);
    }

    /**
     * 获取国际化消息
     *
     * @param code           code
     * @param defaultMessage defaultMessage
     * @param args           args
     * @return i18n Message
     */
    public static String getMessageWithDefault(String code, String defaultMessage, Object... args) {
        return Objects.isNull(applicationContext) ? StringUtils.defaultIfEmpty(code, defaultMessage) :
                applicationContext.getMessage(code, args, defaultMessage, LocaleContextHolder.getLocale());
    }

    /**
     * applicationContext destroy
     */
    @Override
    public void destroy() {
        if (log.isDebugEnabled()) {
            log.debug("applicationContext destroy......");
        }
        applicationContext = null;
    }


}
